home *** CD-ROM | disk | FTP | other *** search
/ Aminet 45 / Aminet 45 (2001)(GTI - Schatztruhe)[!][Oct 2001].iso / Aminet / gfx / x11 / x3270_3_2_16.lha / amiga_src / select.c < prev    next >
C/C++ Source or Header  |  2009-02-27  |  28KB  |  1,232 lines

  1. /*
  2.  * Copyright 1993, 1994, 1995, 1999, 2000 by Paul Mattes.
  3.  *  Permission to use, copy, modify, and distribute this software and its
  4.  *  documentation for any purpose and without fee is hereby granted,
  5.  *  provided that the above copyright notice appear in all copies and that
  6.  *  both that copyright notice and this permission notice appear in
  7.  *  supporting documentation.
  8.  */
  9.  
  10. /*
  11.  * Portions of this code were taken from xterm/button.c:
  12.  * Copyright 1987 by Digital Equipment Corporation, Maynard, Massachusetts.
  13.  *
  14.  *                         All Rights Reserved
  15.  *
  16.  * Permission to use, copy, modify, and distribute this software and its
  17.  * documentation for any purpose and without fee is hereby granted,
  18.  * provided that the above copyright notice appear in all copies and that
  19.  * both that copyright notice and this permission notice appear in
  20.  * supporting documentation, and that the name of Digital Equipment
  21.  * Corporation not be used in advertising or publicity pertaining to
  22.  * distribution of the software without specific, written prior permission.
  23.  *
  24.  *
  25.  * DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
  26.  * ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
  27.  * DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
  28.  * ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
  29.  * WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
  30.  * ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
  31.  * SOFTWARE.
  32.  */
  33.  
  34. /*
  35.  *    select.c
  36.  *        This module handles selections.
  37.  */
  38.  
  39. #include "globals.h"
  40. #include <X11/Xatom.h>
  41. #include <X11/Xmu/Atoms.h>
  42. #include <X11/Xmu/StdSel.h>
  43. #include "3270ds.h"
  44. #include "appres.h"
  45. #include "ctlr.h"
  46. #include "screen.h"
  47. #include "cg.h"
  48. #include "resources.h"
  49.  
  50. #include "actionsc.h"
  51. #include "ctlrc.h"
  52. #include "kybdc.h"
  53. #include "popupsc.h"
  54. #include "screenc.h"
  55. #include "selectc.h"
  56. #include "tablesc.h"
  57. #include "utilc.h"
  58.  
  59. #define Max(x, y)    (((x) > (y))? (x): (y))
  60. #define Min(x, y)    (((x) < (y))? (x): (y))
  61.  
  62. /*
  63.  * Mouse side.
  64.  */
  65.  
  66. /* A button click establishes the boundaries of the 'fixed' area. */
  67. static int      f_start = 0;    /* 'fixed' area */
  68. static int      f_end = 0;
  69.  
  70. /* Mouse motion moves the boundaries of the 'varying' area. */
  71. static int      v_start = 0;    /* 'varying' area */
  72. static int      v_end = 0;
  73.  
  74. static unsigned long down_time = 0;
  75. static unsigned long down1_time = 0;
  76. static Dimension down1_x, down1_y;
  77. static unsigned long up_time = 0;
  78. static int      saw_motion = 0;
  79. static int      num_clicks = 0;
  80. static void grab_sel(int start, int end, Boolean really, Time t);
  81. #define NS        5
  82. static Atom     want_sel[NS];
  83. static struct {            /* owned selections */
  84.     Atom            atom;    /* atom */
  85.     char           *buffer;    /* buffer contents */
  86. }               own_sel[NS];
  87. static Boolean  cursor_moved = False;
  88. static int      saved_cursor_addr;
  89. static void own_sels(Time t);
  90. static int    n_owned = -1;
  91. static Boolean    any_selected = False;
  92.  
  93. extern Widget  *screen;
  94.  
  95. #define CLICK_INTERVAL    200
  96.  
  97. #define event_x(event)        event->xbutton.x
  98. #define event_y(event)        event->xbutton.y
  99. #define event_time(event)    event->xbutton.time
  100.  
  101. #define BOUNDED_XY(event, x, y) {    \
  102.     x = X_TO_COL(event_x(event));    \
  103.     if (x < 0)            \
  104.         x = 0;            \
  105.     if (x >= COLS)            \
  106.         x = COLS - 1;        \
  107.     if (flipped)            \
  108.         x = (COLS - x) - 1;    \
  109.     y = Y_TO_ROW(event_y(event) - *descent);    \
  110.     if (y <= 0)            \
  111.         y = 0;            \
  112.     if (y >= ROWS)            \
  113.         y = ROWS - 1;        \
  114. }
  115.  
  116.  
  117. static int char_class[256] = {
  118. /* nul soh stx etx eot enq ack bel  bs  ht  nl  vt  np  cr  so  si */
  119.     32,  1,  1,  1,  1,  1,  1,  1,  1, 32,  1,  1,  1,  1,  1,  1,
  120. /* dle dc1 dc2 dc3 dc4 nak syn etb can  em sub esc  fs  gs  rs  us */
  121.      1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,
  122. /*  sp   !   "   #   $   %   &   '   (   )   *   +   ,   -   .   / */
  123.     32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
  124. /*   0   1   2   3   4   5   6   7   8   9   :   ;   <   =   >   ? */
  125.     48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 58, 59, 60, 61, 62, 63,
  126. /*   @   A   B   C   D   E   F   G   H   I   J   K   L   M   N   O */
  127.     64, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48,
  128. /*   P   Q   R   S   T   U   V   W   X   Y   Z   [   \   ]   ^   _ */
  129.     48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 91, 92, 93, 94, 48,
  130. /*   `   a   b   c   d   e   f   g   h   i   j   k   l   m   n   o */
  131.     96, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48,
  132. /*   p   q   r   s   t   u   v   w   x   y   z   {   |   }   ~  del */
  133.     48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48,123,124,125,126,   1,
  134. /* ---,---,---,---,---,---,---,---,---,---,---,---,---,---,---,--- */
  135.      1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,
  136. /* ---,---,---,---,---,---,---,---,---,---,---,---,---,---,---,--- */
  137.      1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,  1,
  138. /* nob exc cen ste cur yen bro sec dia cop ord gui not hyp reg mac */
  139.     32,161,162,163,164,165,166,167,168,169,170,171,172,173,174,175,
  140. /* deg plu two thr acu mu  par per ce  one mas gui one one thr que */
  141.    176,177,178,179,180,181,182,183,184,185,186,178,188,189,190,191,
  142. /* Agr Aac Aci Ati Adi Ari AE  Cce Egr Eac Eci Edi Igr Iac Ici Idi */
  143.     48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48,
  144. /* ETH Nti Ogr Oac Oci Oti Odi mul Oob Ugr Uac Uci Udi Yac THO ssh */
  145.     48, 48, 48, 48, 48, 48, 48,215, 48, 48, 48, 48, 48, 48, 48, 48,
  146. /* agr aac aci ati adi ari ae  cce egr eac eci edi igr iac ici idi */
  147.     48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48,
  148. /* eth nti ogr oac oci oti odi div osl ugr uac uci udi yac tho ydi */
  149.     48, 48, 48, 48, 48, 48, 48,247, 48, 48, 48, 48, 48, 48, 48, 48
  150. };
  151.  
  152. /* Parse a charClass string: [low-]high:value[,...] */
  153. void
  154. reclass(char *s)
  155. {
  156.     int n;
  157.     int low, high, value;
  158.     int i;
  159.     char c;
  160.  
  161.     n = -1;
  162.     low = -1;
  163.     high = -1;
  164.     for (;;) {
  165.         c = *s++;
  166.         if (isdigit(c)) {
  167.             if (n == -1)
  168.                 n = 0;
  169.             n = (n * 10) + (c - '0');
  170.             if (n > 255)
  171.                 goto fail;
  172.         } else if (c == '-') {
  173.             if (n == -1 || low != -1)
  174.                 goto fail;
  175.             low = n;
  176.             n = -1;
  177.         } else if (c == ':') {
  178.             if (n == -1)
  179.                 goto fail;
  180.             high = n;
  181.             n = -1;
  182.         } else if (c == ',' || c == '\0') {
  183.             if (n == -1)
  184.                 goto fail;
  185.             value = n;
  186.             n = -1;
  187.             if (high == -1)
  188.                 goto fail;
  189.             if (low == -1)
  190.                 low = high;
  191.             if (high < low)
  192.                 goto fail;
  193.             for (i = low; i <= high; i++)
  194.                 char_class[i] = value;
  195.             low = -1;
  196.             high = -1;
  197.             if (c == '\0')
  198.                 return;
  199.         } else
  200.             goto fail;
  201.     }
  202.  
  203.     fail:
  204.     xs_warning("Error in %s string", ResCharClass);
  205. }
  206.  
  207. static void
  208. select_word(int baddr, Time t)
  209. {
  210.     unsigned char fa = *get_field_attribute(baddr);
  211.     unsigned char ch;
  212.     int class;
  213.  
  214.     /* Find the initial character class */
  215.     if (FA_IS_ZERO(fa))
  216.         ch = CG_space;
  217.     else
  218.         ch = screen_buf[baddr];
  219.     class = char_class[cg2asc[ch]];
  220.  
  221.     /* Find the beginning */
  222.     for (f_start = baddr; f_start % COLS; f_start--) {
  223.         fa = *get_field_attribute(f_start);
  224.         if (FA_IS_ZERO(fa))
  225.             ch = CG_space;
  226.         else
  227.             ch = screen_buf[f_start];
  228.         if (char_class[cg2asc[ch]] != class) {
  229.             f_start++;
  230.             break;
  231.         }
  232.     }
  233.  
  234.     /* Find the end */
  235.     for (f_end = baddr; (f_end+1) % COLS; f_end++) {
  236.         fa = *get_field_attribute(f_end);
  237.         if (FA_IS_ZERO(fa))
  238.             ch = CG_space;
  239.         else
  240.             ch = screen_buf[f_end];
  241.         if (char_class[cg2asc[ch]] != class) {
  242.             f_end--;
  243.             break;
  244.         }
  245.     }
  246.  
  247.     v_start = f_start;
  248.     v_end = f_end;
  249.     grab_sel(f_start, f_end, True, t);
  250. }
  251.  
  252. static void
  253. select_line(int baddr, Time t)
  254. {
  255.     f_start = baddr - (baddr % COLS);
  256.     f_end = f_start + COLS - 1;
  257.     v_start = f_start;
  258.     v_end = f_end;
  259.     grab_sel(f_start, f_end, True, t);
  260. }
  261.  
  262.  
  263. /*
  264.  * Start a new selection.
  265.  * Usually bound to <Btn1Down>.
  266.  */
  267. void
  268. select_start_action(Widget w, XEvent *event, String *params,
  269.     Cardinal *num_params)
  270. {
  271.     int x, y;
  272.     register int baddr;
  273.  
  274.     action_debug(select_start_action, event, params, num_params);
  275.     if (event == NULL) {
  276.         popup_an_error("%s can only be used as a keymap action",
  277.             action_name(select_start_action));
  278.         return;
  279.     }
  280.     if (w != *screen)
  281.         return;
  282.     BOUNDED_XY(event, x, y);
  283.     baddr = ROWCOL_TO_BA(y, x);
  284.     f_start = f_end = v_start = v_end = baddr;
  285.     down1_time = down_time = event_time(event);
  286.     down1_x = event_x(event);
  287.     down1_y = event_y(event);
  288.     if (down_time - up_time > CLICK_INTERVAL) {
  289.         num_clicks = 0;
  290.         /* Commit any previous cursor move. */
  291.         cursor_moved = False;
  292.     }
  293.     if (num_clicks == 0)
  294.         unselect(0, ROWS*COLS);
  295. }
  296.  
  297. /*
  298.  * Alternate form of select_start, which combines cursor motion with selection.
  299.  * Usually bound to <Btn1Down> in a user-specified keymap.
  300.  */
  301. void
  302. move_select_action(Widget w, XEvent *event, String *params,
  303.     Cardinal *num_params)
  304. {
  305.     int x, y;
  306.     register int baddr;
  307.  
  308.     action_debug(move_select_action, event, params, num_params);
  309.     if (event == NULL) {
  310.         popup_an_error("%s can only be used as a keymap action",
  311.             action_name(move_select_action));
  312.         return;
  313.     }
  314.     if (w != *screen)
  315.         return;
  316.     BOUNDED_XY(event, x, y);
  317.     baddr = ROWCOL_TO_BA(y, x);
  318.  
  319.     f_start = f_end = v_start = v_end = baddr;
  320.     down1_time = down_time = event_time(event);
  321.     down1_x = event_x(event);
  322.     down1_y = event_y(event);
  323.  
  324.     if (down_time - up_time > CLICK_INTERVAL) {
  325.         num_clicks = 0;
  326.         /* Commit any previous cursor move. */
  327.         cursor_moved = False;
  328.     }
  329.     if (num_clicks == 0) {
  330.         if (any_selected) {
  331.             unselect(0, ROWS*COLS);
  332.         } else {
  333.             cursor_moved = True;
  334.             saved_cursor_addr = cursor_addr;
  335.             cursor_move(baddr);
  336.         }
  337.     }
  338. }
  339.  
  340. /*
  341.  * Begin extending the current selection.
  342.  * Usually bound to <Btn3Down>.
  343.  */
  344. void
  345. start_extend_action(Widget w, XEvent *event, String *params,
  346.     Cardinal *num_params)
  347. {
  348.     int x, y;
  349.     int baddr;
  350.     Boolean continuous = (!ever_3270 && !toggled(RECTANGLE_SELECT));
  351.  
  352.     action_debug(start_extend_action, event, params, num_params);
  353.     if (event == NULL) {
  354.         popup_an_error("%s can only be used as a keymap action",
  355.             action_name(start_extend_action));
  356.         return;
  357.     }
  358.     if (w != *screen)
  359.         return;
  360.  
  361.     down1_time = 0L;
  362.  
  363.     BOUNDED_XY(event, x, y);
  364.     baddr = ROWCOL_TO_BA(y, x);
  365.  
  366.     if (continuous) {
  367.         /* Think linearly. */
  368.         if (baddr < f_start)
  369.             v_start = baddr;
  370.         else if (baddr > f_end)
  371.             v_end = baddr;
  372.         else if (baddr - f_start > f_end - baddr)
  373.             v_end = baddr;
  374.         else
  375.             v_start = baddr;
  376.     } else {
  377.         /* Think rectangularly. */
  378.         int nrow = baddr / COLS;
  379.         int ncol = baddr % COLS;
  380.         int vrow_ul = v_start / COLS;
  381.         int vrow_lr = v_end / COLS;
  382.         int vcol_ul = Min(v_start % COLS, v_end % COLS);
  383.         int vcol_lr = Max(v_start % COLS, v_end % COLS);
  384.  
  385.         /* Set up the row. */
  386.         if (nrow <= vrow_ul)
  387.             vrow_ul = nrow;
  388.         else if (nrow >= vrow_lr)
  389.             vrow_lr = nrow;
  390.         else if (nrow - vrow_ul > vrow_lr - nrow)
  391.             vrow_lr = nrow;
  392.         else
  393.             vrow_ul = nrow;
  394.  
  395.         /* Set up the column. */
  396.         if (ncol <= vcol_ul)
  397.             vcol_ul = ncol;
  398.         else if (ncol >= vcol_lr)
  399.             vcol_lr = ncol;
  400.         else if (ncol - vcol_ul > vcol_lr - ncol)
  401.             vcol_lr = ncol;
  402.         else
  403.             vcol_ul = ncol;
  404.  
  405.         v_start = (vrow_ul * COLS) + vcol_ul;
  406.         v_end = (vrow_lr * COLS) + vcol_lr;
  407.     }
  408.  
  409.     grab_sel(v_start, v_end, True, event_time(event));
  410.     saw_motion = 1;
  411.     num_clicks = 0;
  412. }
  413.  
  414. /*
  415.  * Continuously extend the current selection.
  416.  * Usually bound to <Btn1Motion> and <Btn3Motion>.
  417.  */
  418. void
  419. select_extend_action(Widget w, XEvent *event, String *params,
  420.     Cardinal *num_params)
  421. {
  422.     int x, y;
  423.     int baddr;
  424.  
  425.     action_debug(select_extend_action, event, params, num_params);
  426.     if (event == NULL) {
  427.         popup_an_error("%s can only be used as a keymap action",
  428.             action_name(select_extend_action));
  429.         return;
  430.     }
  431.     if (w != *screen)
  432.         return;
  433.  
  434.     /* Ignore initial drag events if are too near. */
  435.     if (down1_time != 0L &&
  436.         abs((int) event_x(event) - (int) down1_x) < *char_width &&
  437.         abs((int) event_y(event) - (int) down1_y) < *char_height)
  438.         return;
  439.     else
  440.         down1_time = 0L;
  441.  
  442.     /* If we moved the 3270 cursor on the first click, put it back. */
  443.     if (cursor_moved) {
  444.         cursor_move(saved_cursor_addr);
  445.         cursor_moved = False;
  446.     }
  447.  
  448.     BOUNDED_XY(event, x, y);
  449.     baddr = ROWCOL_TO_BA(y, x);
  450.  
  451.     /*
  452.      * If baddr falls outside if the v range, open up the v range.  In
  453.      * addition, if we are extending one end of the v range, make sure the
  454.      * other end at least covers the f range.
  455.      */
  456.     if (baddr <= v_start) {
  457.         v_start = baddr;
  458.         v_end = f_end;
  459.     }
  460.     if (baddr >= v_end) {
  461.         v_end = baddr;
  462.         v_start = f_start;
  463.     }
  464.  
  465.     /*
  466.      * If baddr falls within the v range, narrow up the nearer end of the
  467.      * v range.
  468.      */
  469.     if (baddr > v_start && baddr < v_end) {
  470.         if (baddr - v_start < v_end - baddr)
  471.             v_start = baddr;
  472.         else
  473.             v_end = baddr;
  474.     }
  475.  
  476.     num_clicks = 0;
  477.     saw_motion = 1;
  478.     grab_sel(v_start, v_end, False, event_time(event));
  479. }
  480.  
  481. /*
  482.  * End the selection.
  483.  * Usually bound to <BtnUp>.
  484.  */
  485. void
  486. select_end_action(Widget w unused, XEvent *event, String *params,
  487.     Cardinal *num_params)
  488. {
  489.     Cardinal i;
  490.     int x, y;
  491.  
  492.     action_debug(select_end_action, event, params, num_params);
  493.     if (event == NULL) {
  494.         popup_an_error("%s can only be used as a keymap action",
  495.             action_name(select_end_action));
  496.         return;
  497.     }
  498.     if (w != *screen)
  499.         return;
  500.  
  501.     if (n_owned == -1) {
  502.         for (i = 0; i < NS; i++)
  503.             own_sel[i].atom = None;
  504.         n_owned = 0;
  505.     }
  506.     for (i = 0; i < NS; i++)
  507.         if (i < *num_params)
  508.             want_sel[i] = XInternAtom(display, params[i], False);
  509.         else
  510.             want_sel[i] = None;
  511.     if (*num_params == 0)
  512.         want_sel[0] = XA_PRIMARY;
  513.  
  514.     BOUNDED_XY(event, x, y);
  515.     up_time = event_time(event);
  516.  
  517.     if (up_time - down_time > CLICK_INTERVAL)
  518.         num_clicks = 0;
  519.  
  520.     if (++num_clicks > 3)
  521.         num_clicks = 1;
  522.  
  523.     switch (num_clicks) {
  524.         case 1:
  525.         if (saw_motion) {
  526.             f_start = v_start;
  527.             f_end = v_end;
  528.             grab_sel(f_start, f_end, True, event_time(event));
  529.         }
  530.         break;
  531.         case 2:
  532.         /*
  533.          * If we moved the 3270 cursor on the first click, put it back.
  534.          */
  535.         if (cursor_moved) {
  536.             cursor_move(saved_cursor_addr);
  537.             cursor_moved = False;
  538.         }
  539.         select_word(f_start, event_time(event));
  540.         break;
  541.         case 3:
  542.         select_line(f_start, event_time(event));
  543.         break;
  544.     }
  545.     saw_motion = 0;
  546. }
  547.  
  548. /*
  549.  * Set the selection.
  550.  * Usually bound to the Copy key.
  551.  */
  552. void
  553. set_select_action(Widget w unused, XEvent *event, String *params,
  554.     Cardinal *num_params)
  555. {
  556.     Cardinal i;
  557.  
  558.     action_debug(set_select_action, event, params, num_params);
  559.  
  560.     if (!any_selected)
  561.         return;
  562.     if (n_owned == -1) {
  563.         for (i = 0; i < NS; i++)
  564.             own_sel[i].atom = None;
  565.         n_owned = 0;
  566.     }
  567.     for (i = 0; i < NS; i++)
  568.         if (i < *num_params)
  569.             want_sel[i] = XInternAtom(display, params[i], False);
  570.         else
  571.             want_sel[i] = None;
  572.     if (*num_params == 0)
  573.         want_sel[0] = XA_PRIMARY;
  574.     own_sels(event_time(event));
  575. }
  576.  
  577. /*
  578.  * Translate the mouse position to a buffer address.
  579.  */
  580. int
  581. mouse_baddr(Widget w, XEvent *event)
  582. {
  583.     int x, y;
  584.  
  585.     if (w != *screen)
  586.         return 0;
  587.     BOUNDED_XY(event, x, y);
  588.     return ROWCOL_TO_BA(y, x);
  589. }
  590.  
  591. /*
  592.  * Cut action.
  593.  * For now, merely erases all unprotected characters currently selected.
  594.  * In future, it may interact more with selections.
  595.  */
  596. #define ULS    sizeof(unsigned long)
  597. #define ULBS    (ULS * 8)
  598.  
  599. void
  600. Cut_action(Widget w unused, XEvent *event, String *params, Cardinal *num_params)
  601. {
  602.     register int baddr;
  603.     unsigned char fa = *get_field_attribute(0);
  604.     unsigned long *target;
  605.     register unsigned char repl;
  606.  
  607.     action_debug(Cut_action, event, params, num_params);
  608.  
  609.     target = (unsigned long *)XtCalloc(ULS, ((ROWS*COLS)+(ULBS-1))/ULBS);
  610.  
  611.     /* Identify the positions to empty. */
  612.     for (baddr = 0; baddr < ROWS*COLS; baddr++) {
  613.         unsigned char c = screen_buf[baddr];
  614.  
  615.         if (IS_FA(c))
  616.             fa = c;
  617.         else if ((IN_ANSI || !FA_IS_PROTECTED(fa)) && SELECTED(baddr))
  618.             target[baddr/ULBS] |= 1 << (baddr%ULBS);
  619.     }
  620.  
  621.     /* Erase them. */
  622.     if (IN_3270)
  623.         repl = CG_null;
  624.     else
  625.         repl = CG_space;
  626.     for (baddr = 0; baddr < ROWS*COLS; baddr++)
  627.         if (target[baddr/ULBS] & (1 << (baddr%ULBS)))
  628.             ctlr_add(baddr, repl, 0);
  629.  
  630.     XtFree((XtPointer)target);
  631. }
  632.  
  633. /*
  634.  * KybdSelect action.  Extends the selection area in the indicated direction.
  635.  */
  636. void
  637. KybdSelect_action(Widget w unused, XEvent *event, String *params,
  638.     Cardinal *num_params)
  639. {
  640.     enum { UP, DOWN, LEFT, RIGHT } direction;
  641.     int x_start, x_end;
  642.     int i;
  643.  
  644.     action_debug(KybdSelect_action, event, params, num_params);
  645.     if (event == NULL) {
  646.         popup_an_error("%s can only be used as a keymap action",
  647.             action_name(select_start_action));
  648.         return;
  649.     }
  650.     if (w != *screen)
  651.         return;
  652.  
  653.     if (*num_params < 1) {
  654.         popup_an_error("%s requires one argument",
  655.             action_name(KybdSelect_action));
  656.         return;
  657.     }
  658.     if (!strcasecmp(params[0], "Up")) {
  659.         direction = UP;
  660.     } else if (!strcasecmp(params[0], "Down")) {
  661.         direction = DOWN;
  662.     } else if (!strcasecmp(params[0], "Left")) {
  663.         direction = LEFT;
  664.     } else if (!strcasecmp(params[0], "Right")) {
  665.         direction = RIGHT;
  666.     } else {
  667.         popup_an_error("%s first argument must be Up, Down, Left, or "
  668.             "Right", action_name(KybdSelect_action));
  669.         return;
  670.     }
  671.  
  672.     if (!any_selected)
  673.         x_start = x_end = cursor_addr;
  674.     else {
  675.         if (f_start < f_end) {
  676.             x_start = f_start;
  677.             x_end = f_end;
  678.         } else {
  679.             x_start = f_end;
  680.             x_end = f_start;
  681.         }
  682.     }
  683.  
  684.     switch (direction) {
  685.         case UP:
  686.         if (!(x_start / COLS))
  687.             return;
  688.         x_start -= COLS;
  689.         break;
  690.         case DOWN:
  691.         if ((x_end / COLS) == ROWS - 1)
  692.             return;
  693.         x_end += COLS;
  694.         break;
  695.         case LEFT:
  696.         if (!(x_start % COLS))
  697.             return;
  698.         x_start--;
  699.         break;
  700.         case RIGHT:
  701.         if ((x_end % COLS) == COLS - 1)
  702.             return;
  703.         x_end++;
  704.         break;
  705.     }
  706.  
  707.     /* Figure out the atoms they want. */
  708.     if (n_owned == -1) {
  709.         for (i = 0; i < NS; i++)
  710.             own_sel[i].atom = None;
  711.         n_owned = 0;
  712.     }
  713.     for (i = 1; i < NS; i++)
  714.         if (i < *num_params)
  715.             want_sel[i] = XInternAtom(display, params[i], False);
  716.         else
  717.             want_sel[i] = None;
  718.     if (*num_params == 1)
  719.         want_sel[0] = XA_PRIMARY;
  720.  
  721.     /* Grab the selection. */
  722.     f_start = v_start = x_start;
  723.     f_end = v_end = x_end;
  724.     grab_sel(f_start, f_end, True, event_time(event));
  725. }
  726.  
  727. /*
  728.  * unselect action.  Removes a selection.
  729.  */
  730. void
  731. Unselect_action(Widget w unused, XEvent *event, String *params,
  732.     Cardinal *num_params)
  733. {
  734.     action_debug(Unselect_action, event, params, num_params);
  735.  
  736.     /* It's just cosmetic. */
  737.     unselect(0, ROWS*COLS);
  738. }
  739.  
  740.  
  741. /*
  742.  * Screen side.
  743.  */
  744.  
  745. static char    *select_buf = CN;
  746. static char    *sb_ptr = CN;
  747. static int      sb_size = 0;
  748. #define SB_CHUNK    1024
  749.  
  750. static Time     sel_time;
  751.  
  752. static void
  753. init_select_buf(void)
  754. {
  755.     if (select_buf == CN)
  756.         select_buf = XtMalloc(sb_size = SB_CHUNK);
  757.     sb_ptr = select_buf;
  758. }
  759.  
  760. static void
  761. store_sel(char c)
  762. {
  763.     if (sb_ptr - select_buf >= sb_size) {
  764.         sb_size += SB_CHUNK;
  765.         select_buf = XtRealloc(select_buf, sb_size);
  766.         sb_ptr = select_buf + sb_size - SB_CHUNK;
  767.     }
  768.     *(sb_ptr++) = c;
  769. }
  770.  
  771. static Boolean
  772. convert_sel(Widget w, Atom *selection, Atom *target, Atom *type,
  773.     XtPointer *value, unsigned long *length, int *format)
  774. {
  775.     int i;
  776.  
  777.     /* Find the right selection. */
  778.     for (i = 0; i < NS; i++)
  779.         if (own_sel[i].atom == *selection)
  780.             break;
  781.     if (i >= NS)    /* not my selection */
  782.         return False;
  783.  
  784.     if (*target == XA_TARGETS(display)) {
  785.         Atom* targetP;
  786.         Atom* std_targets;
  787.         unsigned long std_length;
  788.  
  789.         XmuConvertStandardSelection(w, sel_time, selection,
  790.             target, type, (caddr_t*) &std_targets, &std_length, format);
  791.         *length = std_length + 5;
  792.         *value = (XtPointer) XtMalloc(sizeof(Atom) * (*length));
  793.         targetP = *(Atom**)value;
  794.         *targetP++ = XA_STRING;
  795.         *targetP++ = XA_TEXT(display);
  796.         *targetP++ = XA_COMPOUND_TEXT(display);
  797.         *targetP++ = XA_LENGTH(display);
  798.         *targetP++ = XA_LIST_LENGTH(display);
  799.         (void) memmove(targetP,  std_targets,
  800.                    (int) (sizeof(Atom) * std_length));
  801.         XtFree((char *) std_targets);
  802.         *type = XA_ATOM;
  803.         *format = 32;
  804.         return True;
  805.     }
  806.  
  807.     if (*target == XA_STRING ||
  808.         *target == XA_TEXT(display) ||
  809.         *target == XA_COMPOUND_TEXT(display)) {
  810.         if (*target == XA_COMPOUND_TEXT(display))
  811.             *type = *target;
  812.         else
  813.             *type = XA_STRING;
  814.         *length = strlen(own_sel[i].buffer);
  815.         *value = XtMalloc(*length);
  816.         (void) memmove(*value, own_sel[i].buffer, (int) *length);
  817.         *format = 8;
  818.         return True;
  819.     }
  820.     if (*target == XA_LIST_LENGTH(display)) {
  821.         *value = XtMalloc(4);
  822.         if (sizeof(long) == 4)
  823.             *(long *)*value = 1;
  824.         else {
  825.             long temp = 1;
  826.             (void) memmove(*value,
  827.                     ((char*) &temp) + sizeof(long) - 4,
  828.                     4);
  829.         }
  830.         *type = XA_INTEGER;
  831.         *length = 1;
  832.         *format = 32;
  833.         return True;
  834.     }
  835.     if (*target == XA_LENGTH(display)) {
  836.         *value = XtMalloc(4);
  837.         if (sizeof(long) == 4)
  838.             *(long*)*value = strlen(own_sel[i].buffer);
  839.         else {
  840.             long temp = strlen(own_sel[i].buffer);
  841.             (void) memmove(*value,
  842.                                ((char *) &temp) + sizeof(long) - 4,
  843.                        4);
  844.         }
  845.         *type = XA_INTEGER;
  846.         *length = 1;
  847.         *format = 32;
  848.         return True;
  849.     }
  850.  
  851.     if (XmuConvertStandardSelection(w, sel_time, selection,
  852.         target, type, (caddr_t *)value, length, format))
  853.         return True;
  854.  
  855.     /* else */
  856. #if 0
  857.     printf("Unknown conversion request: %s to %s\n",
  858.         XGetAtomName(display, *selection),
  859.         XGetAtomName(display, *target));
  860. #endif
  861.     return False;
  862. }
  863.  
  864. static void
  865. lose_sel(Widget w unused, Atom *selection)
  866. {
  867.     int i;
  868.  
  869.     for (i = 0; i < NS; i++)
  870.         if (own_sel[i].atom != None && own_sel[i].atom == *selection) {
  871.             own_sel[i].atom = None;
  872.             XtFree(own_sel[i].buffer);
  873.             own_sel[i].buffer = CN;
  874.             n_owned--;
  875.             break;
  876.         }
  877.     if (!n_owned)
  878.         unselect(0, ROWS*COLS);
  879. }
  880.  
  881. /*
  882.  * Somewhat convoluted logic to return an ASCII character for a given screen
  883.  * position.
  884.  *
  885.  * The character has to be found indirectly from screen_buf and the field
  886.  * attirbutes, so that zero-intensity fields become blanks.
  887.  */
  888. static Boolean osc_valid = False;
  889.  
  890. static void
  891. osc_start(void)
  892. {
  893.     osc_valid = False;
  894. }
  895.  
  896. /*
  897.  * Return a 'selection' version of a given character on the screen.
  898.  * Returns a printable ASCII character, or 0 if the character is a NULL and
  899.  * shouldn't be included in the selection.
  900.  * Also returns in 'ge' whether or not an ESC (0x1b) should be included
  901.  * in front of the character.
  902.  */
  903. static unsigned char
  904. onscreen_char(int baddr, Boolean *ge)
  905. {
  906.     static int osc_baddr;
  907.     static unsigned char fa;
  908.  
  909.     /* If we aren't moving forward, all bets are off. */
  910.     if (osc_valid && baddr < osc_baddr)
  911.         osc_valid = False;
  912.  
  913.     if (osc_valid) {
  914.         /*
  915.          * Search for a new field attribute between the address we
  916.          * want and the last address we searched.  If we found a new
  917.          * field attribute, save the address for next time.
  918.          */
  919.         (void) get_bounded_field_attribute(baddr, osc_baddr, &fa);
  920.         osc_baddr = baddr;
  921.     } else {
  922.         /*
  923.          * Find the attribute the old way.
  924.          */
  925.         fa = *get_field_attribute(baddr);
  926.         osc_baddr = baddr;
  927.         osc_valid = True;
  928.     }
  929.  
  930.     /* Assume it isn't a graphic escape. */
  931.     *ge = False;
  932.  
  933.     /* If it isn't visible, then make it a blank. */
  934.     if (FA_IS_ZERO(fa))
  935.         return ' ';
  936.  
  937.     switch (ea_buf[baddr].cs) {
  938.         case 0:
  939.         default:
  940.         switch (screen_buf[baddr]) {
  941.             case CG_null:
  942.             return 0;
  943.             case CG_fm:
  944.             *ge = True;
  945.             return ';';
  946.             case CG_dup:
  947.             *ge = True;
  948.             return '*';
  949.             default:
  950.             return cg2asc[screen_buf[baddr]];
  951.         }
  952.         case CS_GE:
  953.         switch (screen_buf[baddr]) {
  954.             case CG_null:
  955.             return 0;
  956.             case CG_Yacute:
  957.             return '[';
  958.             case CG_diaeresis:
  959.             return ']';
  960.             default:
  961.             *ge = True;
  962.             return cg2asc[screen_buf[baddr]];
  963.         }
  964.         case 2:
  965.         /* vt100 line-drawing character */
  966.         return screen_buf[baddr] + 0x5f;
  967.     }
  968. }
  969.  
  970. /*
  971.  * Attempt to own the selections in want_sel[].
  972.  */
  973. static void
  974. own_sels(Time t)
  975. {
  976.     register int i, j;
  977.  
  978.     /*
  979.      * Try to grab any new selections we may want.
  980.      */
  981.     for (i = 0; i < NS; i++) {
  982.         Boolean already_own = False;
  983.  
  984.         if (want_sel[i] == None)
  985.             continue;
  986.  
  987.         /* Check if we already own it. */
  988.         for (j = 0; j < NS; j++)
  989.             if (own_sel[j].atom == want_sel[i]) {
  990.                 already_own = True;
  991.                 break;
  992.             }
  993.  
  994.         /* Find the slot for it. */
  995.         if (!already_own) {
  996.             for (j = 0; j < NS; j++)
  997.                 if (own_sel[j].atom == None)
  998.                     break;
  999.             if (j >= NS)
  1000.                 continue;
  1001.         }
  1002.  
  1003.         if (XtOwnSelection(*screen, want_sel[i], t, convert_sel,
  1004.             lose_sel, NULL)) {
  1005.             if (!already_own) {
  1006.                 n_owned++;
  1007.                 own_sel[j].atom = want_sel[i];
  1008.             }
  1009.             if (own_sel[j].buffer != CN)
  1010.                 XtFree(own_sel[j].buffer);
  1011.             own_sel[j].buffer = XtMalloc(strlen(select_buf) + 1);
  1012.             (void) memmove(own_sel[j].buffer, select_buf,
  1013.                 strlen(select_buf) + 1);
  1014.         } else {
  1015.             XtWarning("Could not get selection");
  1016.             if (own_sel[j].atom != None) {
  1017.                 XtFree(own_sel[j].buffer);
  1018.                 own_sel[j].buffer = CN;
  1019.                 own_sel[j].atom = None;
  1020.                 n_owned--;
  1021.             }
  1022.         }
  1023.     }
  1024.     if (!n_owned)
  1025.         unselect(0, ROWS*COLS);
  1026.     sel_time = t;
  1027. }
  1028.  
  1029. /*
  1030.  * Copy the selected area on the screen into a buffer and attempt to
  1031.  * own the selections in want_sel[].
  1032.  */
  1033. static void
  1034. grab_sel(int start, int end, Boolean really, Time t)
  1035. {
  1036.     register int i;
  1037.     int start_row, end_row;
  1038.     unsigned char osc;
  1039.     Boolean ge;
  1040.  
  1041.     unselect(0, ROWS*COLS);
  1042.  
  1043.     if (start > end) {
  1044.         int exch = end;
  1045.  
  1046.         end = start;
  1047.         start = exch;
  1048.     }
  1049.  
  1050.     start_row = start / COLS;
  1051.     end_row = end / COLS;
  1052.  
  1053.     init_select_buf();    /* prime the store_sel() routine */
  1054.     osc_start();        /* prime the onscreen_char() routine */
  1055.  
  1056.     if (!ever_3270 && !toggled(RECTANGLE_SELECT)) {
  1057.         /* Continuous selections */
  1058.         for (i = start; i <= end; i++) {
  1059.             SET_SELECT(i);
  1060.             if (really) {
  1061.                 if (i != start && !(i % COLS))
  1062.                     store_sel('\n');
  1063.                 osc = onscreen_char(i, &ge);
  1064.                 if (osc) {
  1065.                     if (ge)
  1066.                         store_sel('\033');
  1067.                     store_sel((char)osc);
  1068.                 }
  1069.             }
  1070.         }
  1071.         /* Check for newline extension */
  1072.         if ((end % COLS) != (COLS - 1)) {
  1073.             Boolean all_blank = True;
  1074.  
  1075.             for (i = end; i < end + (COLS - (end % COLS)); i++) {
  1076.                 osc = onscreen_char(i, &ge);
  1077.                 if (osc && osc != ' ') {
  1078.                     all_blank = False;
  1079.                     break;
  1080.                 }
  1081.             }
  1082.             if (all_blank) {
  1083.                 for (i = end; i < end + (COLS - (end % COLS)); i++) {
  1084.                     SET_SELECT(i);
  1085.                 }
  1086.                 store_sel('\n');
  1087.             }
  1088.         }
  1089.     } else {
  1090.         /* Rectangular selections */
  1091.         if (start_row == end_row) {
  1092.             for (i = start; i <= end; i++) {
  1093.                 SET_SELECT(i);
  1094.                 if (really) {
  1095.                     osc = onscreen_char(i, &ge);
  1096.                     if (osc) {
  1097.                         if (ge)
  1098.                             store_sel('\033');
  1099.                         store_sel((char)osc);
  1100.                     }
  1101.                 }
  1102.             }
  1103.             if (really && (end % COLS == COLS - 1))
  1104.                 store_sel('\n');
  1105.         } else {
  1106.             int row, col;
  1107.             int start_col = start % COLS;
  1108.             int end_col = end % COLS;
  1109.  
  1110.             if (start_col > end_col) {
  1111.                 int exch = end_col;
  1112.  
  1113.                 end_col = start_col;
  1114.                 start_col = exch;
  1115.             }
  1116.  
  1117.             for (row = start_row; row <= end_row; row++) {
  1118.                 for (col = start_col; col <= end_col; col++) {
  1119.                     SET_SELECT(row*COLS + col);
  1120.                     if (really) {
  1121.                         osc = onscreen_char(row*COLS + col, &ge);
  1122.                         if (osc) {
  1123.                             if (ge)
  1124.                                 store_sel('\033');
  1125.                             store_sel((char)osc);
  1126.                         }
  1127.                     }
  1128.                 }
  1129.                 if (really)
  1130.                     store_sel('\n');
  1131.             }
  1132.         }
  1133.     }
  1134.  
  1135.     /* Terminate the result. */
  1136.     if (really)
  1137.         store_sel('\0');
  1138.  
  1139.     any_selected = True;
  1140.     ctlr_changed(0, ROWS*COLS);
  1141.     if (really)
  1142.         own_sels(t);
  1143. }
  1144.  
  1145. /*
  1146.  * Check if any character in a given region is selected.
  1147.  */
  1148. Boolean
  1149. area_is_selected(int baddr, int len)
  1150. {
  1151.     register int i;
  1152.  
  1153.     for (i = 0; i < len; i++)
  1154.         if (SELECTED(baddr+i))
  1155.             return True;
  1156.     return False;
  1157. }
  1158.  
  1159. /*
  1160.  * Unhighlight the region of selected text -- but don't give up the selection.
  1161.  */
  1162. void
  1163. unselect(int baddr unused, int len unused)
  1164. {
  1165.     if (any_selected) {
  1166.         (void) memset((char *) selected, 0, (ROWS*COLS + 7) / 8);
  1167.         ctlr_changed(0, ROWS*COLS);
  1168.         any_selected = False;
  1169.     }
  1170. }
  1171.  
  1172. /* Selection insertion. */
  1173. #define NP    5
  1174. static Atom    paste_atom[NP];
  1175. static int    n_pasting = 0;
  1176. static int    pix = 0;
  1177. static Time    paste_time;
  1178.  
  1179. static void
  1180. paste_callback(Widget w, XtPointer client_data unused, Atom *selection unused,
  1181.     Atom *type unused, XtPointer value, unsigned long *length,
  1182.     int *format unused)
  1183. {
  1184.     char *s;
  1185.     unsigned long len;
  1186.  
  1187.     if ((value == NULL) || (*length == 0)) {
  1188.         XtFree(value);
  1189.  
  1190.         /* Try the next one. */
  1191.         if (n_pasting > pix)
  1192.             XtGetSelectionValue(w, paste_atom[pix++], XA_STRING,
  1193.                 paste_callback, NULL, paste_time);
  1194.         return;
  1195.     }
  1196.  
  1197.     s = (char *)value;
  1198.     len = *length;
  1199.     (void) emulate_input(s, (int) len, True);
  1200.     n_pasting = 0;
  1201.  
  1202.     XtFree(value);
  1203. }
  1204.  
  1205. void
  1206. insert_selection_action(Widget w, XEvent *event, String *params,
  1207.     Cardinal *num_params)
  1208. {
  1209.     Cardinal i;
  1210.     Atom    a;
  1211.     XButtonEvent *be = (XButtonEvent *)event;
  1212.  
  1213.     action_debug(insert_selection_action, event, params, num_params);
  1214.     n_pasting = 0;
  1215.     for (i = 0; i < *num_params; i++) {
  1216.         a = XInternAtom(display, params[i], True);
  1217.         if (a == None) {
  1218.             popup_an_error("%s: No atom for selection",
  1219.                 action_name(insert_selection_action));
  1220.             continue;
  1221.         }
  1222.         if (n_pasting < NP)
  1223.             paste_atom[n_pasting++] = a;
  1224.     }
  1225.     pix = 0;
  1226.     if (n_pasting > pix) {
  1227.         paste_time = be->time;
  1228.         XtGetSelectionValue(w, paste_atom[pix++], XA_STRING,
  1229.             paste_callback, NULL, paste_time);
  1230.     }
  1231. }
  1232.